
/*
 *  Vector.h
 *  vector
 *
 *  Created by viktor holmberg on 2010-09-02.
 *  Copyright 2010 __MyCompanyName__. All rights reserved.
 *
 */
#ifndef Vector_h
#define Vector_h
#include <cstring>
#include <iostream>
#include <stdexcept>

template <class T>
class Vector {
public:
    explicit Vector(size_t = 0);
    explicit Vector(size_t, const T);
    Vector(const Vector &);
    ~Vector();
    T & operator[](const size_t);
	const T & operator[](const size_t) const;
    Vector & operator=(const Vector &);
    void push_back(const T&);
    void insert(const size_t, const T&);
    void erase(const size_t);
    void clear();
    size_t size() const;
    void sort(bool = true);
protected:
    size_t data_size;
    size_t next_index;
    T *data;
    void ensure_space();
};

/*
 * Constructor; initializes [init_size] elements
 * with the elements type default value.
 */
template <class T>
Vector<T>::Vector(const size_t init_size) : data_size(init_size), next_index(init_size), data(new T[init_size])
{
	for(size_t i = 0; i<init_size; ++i) {
        data[i] = T();
    }
}

/*
 * Constructor; initializes [init_size] elements
 * with value [init_value].
 */
template <class T>
Vector<T>::Vector(const size_t init_size, const T init_value) : data_size(init_size), next_index(init_size), data(new T[init_size]) {
    for(size_t i = 0; i<init_size; ++i) {
        data[i] = init_value;
    }
}

/*
 * Copy constructor.
 */
template <class T>
Vector<T>::Vector(const Vector & v) : data_size(v.data_size), next_index(v.next_index) {
    data = new T[data_size];
    std::copy(v.data, v.data+next_index, data);
}

/*
 * Overload assignment operator to make deep copy.
 */
template <class T>
Vector<T> & Vector<T>::operator=(const Vector& v) {
    if (&v != this) {
		data_size = v.data_size;
		next_index = v.next_index;
        delete[]data;
        data = new T[data_size];
        std::copy(v.data, v.data+next_index, data); 
    }
    return *this;
}

template <class T>
Vector<T>::~Vector(){
    delete[] data;
}

/*
 * Implementation of the element access operator.
 */
template <class T>
T & Vector<T>::operator[](const size_t index){
    if (index>=next_index){
        throw std::out_of_range("Trying to access index out of bounds");
    }
    return data[index];
}

/*
 * Element access operator overloading.
 */
template <class T>
const T & Vector<T>::operator[](const size_t index) const {
    if (index>=next_index){
        throw std::out_of_range("Trying to access const index out of bounds");
    }
    return data[index];
}

/* 
 * See if we can put another element in the array,
 * if we can't it will re-allocate the array with
 * more memory so that we can.
 */
template <class T>
void Vector<T>::ensure_space() {
    if(next_index>=data_size) {
        size_t new_size=(data_size!=0)? data_size*2 : 1;
        T * new_data = new T[new_size];
        std::copy(data, data+data_size, new_data);
        delete [] data;
        data = new_data;
        data_size = new_size;
    }
}       

/*
 * Inserts an element at the end of the vector.
 * If the array is too small; allocate new space
 * and copy the data there.
 * \param item the item to be inserted.
 */
template <class T>
void Vector<T>::push_back(const T& item) {
    ensure_space();
    data[next_index++] = item;
}

/*
 * Inserts an element at the given index.
 * If the array is too small; allocate new space
 * and copy the data there.
 * \param index where to insert the item.
 * \param item the item to be inserted.
 */
template <class T>
void Vector<T>::insert(const size_t index, const T& item) {
	if (index>next_index){
        throw std::out_of_range("Trying to access insert out of bounds");
    }
    ensure_space();
    //move the elements after index
    size_t i = next_index;
	
	while (i!=0 && i>index) {
		data[i] = data[i-1];
		--i;
	}
	
    //insert element at index
    data[index] = item;
    ++next_index;
}

/*
 * Erases an element at the given index.
 * \param the index to be erased
 */
template <class T>
void Vector<T>::erase(const size_t index) {
	if (index>=next_index){
        throw std::out_of_range("Trying to erase index out of bounds");
    }
    for(size_t i = index; i<next_index; ++i) {
        data[i] = data[i+1];
    }
	--next_index;
}

/*
 * "Removes" all the elements in the vector.
 */
template <class T>
void Vector<T>::clear() {
    next_index = 0;
}

/*
 * Returns the number of elements in the vector.
 * \return the number of elements in the vector.
 */
template <class T>
size_t Vector<T>::size() const{
    return next_index;
}

/*
 * Sorts the array in the order specified
 * \param ascending true if acending, false if decending
 */
template <class T>
void Vector<T>::sort(bool ascending) {
    if(!ascending){
        std::sort(data, data+next_index,std::less<T>());
		std::reverse(data, data+next_index);
    }
    else{
        std::sort(data, data+next_index,std::less<T>());
    }
}

#endif

